	package erfgame.frontend.desktop.view.terrain.iso.handler;


import java.awt.Point;
import java.util.List;

import erfgame.core.Point3D;
import erfgame.core.Point3DAndRotation;
import erfgame.core.dao.ObjectDAO;
import erfgame.core.graph.simple.Node;
import erfgame.core.graphics.renderer.chain.RendererChain;
import erfgame.core.util.SortedList;
import erfgame.core.world.World;
import erfgame.core.world.terrain.BlockSurfaceImages;
import erfgame.core.world.terrain.GridRectangle;
import erfgame.core.world.terrain.GridUtils;
import erfgame.core.world.terrain.insertion.SurfaceInsertion;
import erfgame.core.world.terrain.insertion.SurfaceTerrainImageInsertion;

import erfgame.frontend.desktop.view.terrain.iso.EntitySelection;
import erfgame.frontend.desktop.view.terrain.iso.IsometricTerrainView;

public class GridSelectionInteractionHandler 
	extends BaseGridSelectionInteractionHandler {
	
	private SortedList<Point3D> selectedGridTiles;
	private GridSelectionAction selectionAction;
	private EntitySelection selection;
	
	public GridSelectionInteractionHandler( 
			IsometricTerrainView view, 
			ZLevelSelector zLevelSelector, 
			EntitySelection selection, 
			GridSelectionAction selectionAction) {
		super(view, zLevelSelector);
		this.selectedGridTiles = new SortedList<Point3D>(GridUtils.createGridPoint3DComparator(view), 10);
		this.selection = selection;
		this.selectionAction = selectionAction;
	}
	
	@Override
	protected void highlight( GridRectangle selectionRectangle, int[] zRange ) {
		// work out the grid that contains it
		SortedList<Point3D> toRemove = new SortedList<Point3D>( this.selectedGridTiles );
		List<Point> area = selectionRectangle.getArea();
		int minbz = zRange[0];
		int maxbz = zRange[1];
		for( int i=area.size(); i>0; ) {
			i--;
			Point p = area.get( i );
			for(int bz=minbz; bz<=maxbz; bz++) {
				Point3D p3d = new Point3D(p.x, p.y, bz);
				if( !this.selectedGridTiles.contains(p3d) ) {
					selectBlockPoint( p, bz, true );
					this.selectedGridTiles.add(p3d);
				} else {
					toRemove.remove(p3d);
				}
			}
		}
		for( int i=toRemove.size(); i>0; ) {
			i--;
			Point3D p = toRemove.get( i );
			selectBlockPoint(p, p.z, false);
			this.selectedGridTiles.remove(p);
		}
	}
	
	private void selectBlockPoint( Point p, int bz, boolean selected ) {
		World world = view.getCurrentWorld();
		
		int bx = p.x >> world.getxGridTilesPerBlockShift();
		int by = p.y >> world.getyGridTilesPerBlockShift();
		int lgx = p.x & world.getxGridTilesPerBlockMask();
		int lgy = p.y & world.getyGridTilesPerBlockMask();
	
		selectBlockGrid(bx, by, bz, lgx, lgy, selected);
		boolean endx = lgx == 0 && lgy % 2 != 0;
		boolean endy = lgy == 0;
		if( endy ) {
			selectBlockGrid(bx, by-1, bz, lgx, world.getYGridTilesPerBlock(), selected);
		}
		if( endx ) {
			selectBlockGrid(bx-1, by, bz, world.getXGridTilesPerBlock(), lgy, selected);
		}
		if( endx && endy ) {
			selectBlockGrid( bx-1, by-1, bz, world.getXGridTilesPerBlock(), world.getYGridTilesPerBlock(), selected );
		}		
	}
	
	private void selectBlockGrid( int bx, int by, int bz, int lgx, int lgy, boolean selected ) {
		// some squares exist across multiple blocks
		
		World world = view.getCurrentWorld();
		if( bx >= 0 && by >= 0 && bx < world.getBlocksX() && by < world.getBlocksY() ) {
			ObjectDAO<Point3DAndRotation,BlockSurfaceImages> bsiDAO;
			bsiDAO = view.getSurfaceImagesDAO();
			byte blockGridId = world.getGridId(lgx, lgy);
			Point3DAndRotation key = new Point3DAndRotation( bx, by, bz, view.getCurrentRotation() );
			BlockSurfaceImages bsi = bsiDAO.retrieveObject(key);
			List<Node<SurfaceInsertion>> blockInsertions = bsi.getSurfaceInsertions();
			for( Node<SurfaceInsertion> node : blockInsertions ) {
				SurfaceInsertion insertion = node.getValue();
				if( insertion instanceof SurfaceTerrainImageInsertion ) {
					SurfaceTerrainImageInsertion surfaceInsertion = (SurfaceTerrainImageInsertion)insertion;
					if( surfaceInsertion.getSurfaceImage().getBlockGridId() == blockGridId ) {
						RendererChain rendererChain = surfaceInsertion.getRendererChain();
						if( selected ) {
							selection.addRendererChain(rendererChain, view.getCurrentRotation());
						} else {
							selection.removeRendererChain(rendererChain);
						}
					}
				}
			}
		}
	}
	
	protected void clearHighlight() {
		
		for( int i=this.selectedGridTiles.size(); i>0; ) {
			i--;
			Point3D p = this.selectedGridTiles.get( i );
			selectBlockPoint(p, p.z, false);
		}
		this.selectedGridTiles.clear();
		super.clearHighlight();
	}

	@Override
	protected void select(GridRectangle selectionRectangle, int[] zRange) {
		this.selectionAction.select(selectionRectangle, zRange[0],zRange[1]);
	}
}
